Option Explicit

'################################################################
' Descriptor
' sql 数据生成上下文
'################################################################

'################################################################
' Field
'################################################################
' 最外层的 sql 查询
Private mainSql As ISql

' 真实表名及其表定义之间的关系
Private realTableDefineMap As SimpleMap

' 真实表名及其别名之间的转换关系
Private tableNameMap As SimpleMap

' 各表对应的临时数据表
' 真实表名/子查询的临时名 : 临时数据表
Private dummyDataTableMap As SimpleMap

' 查询层次关系
' List<List<ISql>>
' union 算做一层
Private sqlLevelList As Collection

'################################################################
' Constructor
'################################################################
Private Sub Class_Initialize()
    Set realTableDefineMap = New SimpleMap
    Set tableNameMap = New SimpleMap
    Set dummyDataTableMap = New SimpleMap
    Set sqlLevelList = New Collection
End Sub

Private Sub Class_Terminate()
    Set mainSql = Nothing
    Set realTableDefineMap = Nothing
    Set tableNameMap = Nothing
    Set dummyDataTableMap = Nothing
    Set sqlLevelList = Nothing
End Sub

'################################################################
' 各阶段方法
'################################################################
' 准备阶段
' @param realTableDefineMapVal 真实表名及其表定义之间
' @param mainSqlVal sql 对象
Public Sub Init(ByRef realTableDefineMapVal As SimpleMap, ByRef mainSqlVal As ISql)
    ' 1. 保存 sql
    Set mainSql = mainSqlVal

    ' 2. 保存真实表名及其表定义之间
    Set realTableDefineMap = realTableDefineMapVal

    ' 3. 整理表明转换关系
    ' 在这一步里，先保存真实表之间的关系，别名、子查询的关系在后面的步骤中再保存
    Dim realName As Variant
    For each realName In realTableDefineMap.Keys
        tableNameMap.Add realName & "", realName & ""
    Next

    ' 4. 为真实表生成临时数据表
    Dim realDummyDataTable As RealTableDataTable
    For each realName In realTableDefineMap.Keys
        Set realDummyDataTable = New RealTableDataTable
        realDummyDataTable.Init realTableDefineMap.Item(realName & "")
        dummyDataTableMap.Add realName & "", realDummyDataTable
    Next

    ' 5. 整理查询层次关系
    extratSqlLevel

    ' 6. 遍历查询层次关系，收集所有表的别名与真名的关系
    extractTableRelation

    ' 7. 遍历所有子查询生成对应的临时数据表
End Sub

' ---------------------------------------------------
' 整理查询层次关系
' ---------------------------------------------------
' 解析 mainSql 中个级别的查询
Private Sub extratSqlLevel()
    ' 每一层完成后向队列中写入一个 Nothing
    ' 1. 写入起始对象
    Dim subSelectQueue As ObjQueue
    Set subSelectQueue = New ObjQueue
    subSelectQueue.offer mainSql
    subSelectQueue.offer Nothing

    ' 2. 保存起始级别的sql
    Dim tempLevelCollector As Collection
    Set tempLevelCollector = New Collection
    sqlLevelList.Add tempLevelCollector

    Dim tempSql As ISql
    Dim tempSelectSql As ISql
    Dim tempUnionSql As ISql
    While subSelectQueue.IsEmpty = False
        Set tempSql = subSelectQueue.poll
        If tempSql Is Nothing Then
            ' 3. 如果当前是一个 Nothing，并且队列中还有元素，则说明还有下一层的 sql
            '    所有在这里创建一个新的层次对象
            If subSelectQueue.IsEmpty = False Then
                Set tempLevelCollector = New Collection
                sqlLevelList.Add tempLevelCollector

                ' 当前是 Nothing，但是后面还有元素
                ' 说明这一次已经解析完成，需要在下一次变量中开始下一层的解析
                ' 所以在队列的队尾附加一个 Nothing 标记，作为下一次的结束标记
                subSelectQueue.offer Nothing
            End If
        Else
            ' 4. 如果当前是一个 sql 则进行子查询的解析，只向下再解析一层
            '    更深的层次在下一层次的时候再进行解析
            tempLevelCollector.Add tempSql
            extractSubSql tempSql, subSelectQueue
        End If
    Wend

End Sub

' 解析 sql 中的子 sql，只解析一层
' @param sql 需要解析的sql
' @param subSelectQueue 子sql收集队列
Private Sub extractSubSql(ByRef sql As ISql, ByRef subSelectQueue As ObjQueue)
    If TypeOf sql Is SelectSql Then
        ' 解析 select sql
        extractSubSqlFromSelectSql sql, subSelectQueue
    ElseIf TypeOf sql Is UnionSql Then
        ' 解析 union sql
        extractSubSqlFromUnionSql sql, subSelectQueue
    End If
End Sub

' 解析 select sql 中的子 sql，只解析一层
' @param sql 需要解析的sql
' @param subSelectQueue 子sql收集队列
Private Sub extractSubSqlFromSelectSql(ByRef sql As SelectSql, ByRef subSelectQueue As ObjQueue)
    ' 1. 处理所有的 from + join
    Dim tempFrom As IFromItem
    Dim fromTableDefine As ISqlTableDefine ' 用来保存某个表定义
    Dim cond As SqlCondition ' 用来解析表用到的条件

    For Each tempFrom In sql.FromItems
        Set fromTableDefine = tempFrom.SqlTableDefine

        ' 1.1 检查使用的表是不是子查询
        ' 先判断这个表项使用的表类型，如果不是真实表，则是子查询
        If Not TypeOf fromTableDefine Is RealNameSqlTableDefine Then
            subSelectQueue.offer fromTableDefine
        End If

        ' 1.2 检查所有的条件中是否存在子查询
        '     因为可能存在函数内嵌套子查询的情况，
        '     所以需要打开 SqlCondition 的所有组成元素来检查是否存在子查询
        For Each cond In tempFrom.Conditions
            ' 解析左侧元素
            extractSubSqlFromExpression cond.LeftExpression, subSelectQueue
            ' 解析右侧元素
            extractSubSqlFromExpression cond.RightExpression, subSelectQueue
        Next
    Next

    ' 2. 处理 where 条件
    For Each cond In sql.WhereConditions
        ' 解析左侧元素
        extractSubSqlFromExpression cond.LeftExpression, subSelectQueue
        ' 解析右侧元素
        extractSubSqlFromExpression cond.RightExpression, subSelectQueue
    Next
End Sub

' 解析 union sql 中的子 sql，只解析一层
' @param sql 需要解析的sql
' @param subSelectQueue 子sql收集队列
Private Sub extractSubSqlFromUnionSql(ByRef sql As UnionSql, ByRef subSelectQueue As ObjQueue)
    Dim tempSql As Variant

    ' 遍历 union 内的所有 selectSql
    For Each tempSql In sql.SelectSqlList
        extractSubSql tempSql, subSelectQueue
    Next
End Sub

' 解析 IExpression 表达式中的子查询
Private Sub extractSubSqlFromExpression(ByRef e As IExpression, ByRef subSelectQueue As ObjQueue)
    ' 1. 如果表达式已经是子查询了，则直接保存并退出方法
    If TypeOf e Is SelectSql Or TypeOf e Is UnionSql Then
        subSelectQueue.offer e
        Exit Sub
    End If

    ' 2. 如果是一个嵌套的 sqlCondition 则需要继续按照左右运算符为单位进行解析
    If TypeOf e Is SqlCondition Then
        Dim sc As SqlCondition
        Set sc = e
        ' 迭代当前方法，解析左元素
        extractSubSqlFromExpression sc.LeftExpression, subSelectQueue
        ' 迭代当前方法，解析右元素
        extractSubSqlFromExpression sc.RightExpression, subSelectQueue
        Exit Sub
    End If

    ' 3. 如果是一个函数，需要解析它的每一个参数
    If TypeOf e Is FuncExpression Then
        Dim fe As FuncExpression
        Set fe = e

        Dim pa As IExpression
        ' 迭代当前方法，逐个解析函数参数
        For Each pa In fe.Parameters
            extractSubSqlFromExpression pa, subSelectQueue
        Next
    End If

    ' 4. 对于其他情况: 列定义、数字、字符串 全部忽略
End Sub

' ---------------------------------------------------
' 遍历查询层次关系，收集所有表的别名与真名的关系
' ---------------------------------------------------
' 遍历查询层次关系，收集所有表的别名与真名的关系
Private Sub extractTableRelation()
    Dim selectSqls As Collection
    Dim sql As ISql
    For Each selectSqls In sqlLevelList
        For Each sql In selectSqls
            If TypeOf sql Is SelectSql Then
                ' 解析 select sql
                extractTableRelationFromSelectSql sql
            ElseIf TypeOf sql Is UnionSql Then
                ' 解析 union sql
                extractTableRelationFromUnionSql sql
            End If
        Next
    Next
End Sub

' 收集 selectSql 中的所有表的别名与真名的关系
Private Sub extractTableRelationFromSelectSql(ByRef sql As SelectSql)
    ' 1. 先将自己的名称保存到缓存中，这里不区分是不是子查询，全部保存（即使是主查询）
    tableNameMap.Add sql.Name, sql.Name

    ' 2. 收集各个表项（From + Join）的名称关系
    Dim tempFrom As IFromItem
    Dim fromTableDefine As ISqlTableDefine ' 用来保存某个表定义

    For Each tempFrom In sql.FromItems
        ' 保存方式: [别名:真名]
        tableNameMap.Add tempFrom.Alias, tempFrom.Name
    Next
End Sub

' 收集 unionSql 中的所有表的别名关系
Private Sub extractTableRelationFromUnionSql(ByRef sql As UnionSql)
    Dim tempSql As Variant
    tableNameMap.Add sql.Name, sql.Name

    ' 遍历 union 内的所有 selectSql
    For Each tempSql In sql.SelectSqlList
        extractTableRelationFromSelectSql tempSql
    Next
End Sub
'################################################################
' Function
'################################################################
' 获取 sql 的最大嵌套深度/嵌套级别
Public Property Get MaxSelectSqlLevel() As Integer
    MaxSelectSqlLevel = selectSqlLevelList.Count
End Property

' 获取指定嵌套级别下的所有 selectSql 对象
' 如果指定级别不存在，则返回 Nothing
' @param level 嵌套级别/嵌套深度（从 1 开始）
' @return List<SelectSql>
Public Function getSelectSqlByLevel(ByRef level As Integer) As Collection
    If level > selectSqlLevelList.Count Then
        Set getSelectSqlByLevel = Nothing
    Else
        Set getSelectSqlByLevel = selectSqlLevelList.Item(level)
    End If
End Property